Cracking The Coding Interview Java篇

相信大部分程序员对于《Cracking The Coding Interview》并不陌生,第五版也在今年出了,这本英文版的程序员面试宝典,绝对值得一读,不只是为了面试,为了丰富自己的编程知识也是很有帮助的。我读了其中的一部分感兴趣的章节,做的一点笔记。

正文:

final ,finally, finalize

final关键字

final关键字根据它的使用场景不同有不同的作用:

  • final变量:你不能改变它的值
final int size = 3;
  • final的method:你不能覆盖该method
class Poof{
final void calfunc(){}
}
  • final的类:你不能继承该类,也就是创建它的子类。
final class MyClass{
}

finally关键字

finally关键字是跟try/catch语句结合在一起使用的。用来存放不管有没有异常,都得执行的程序。如果你没有finally,你得同时把相同的代码放到try和catch两处。

try{
...
}catch(Exception x){
...
}finally{
...
}

值得注意的,如果try/catch块里有return语句,此时的执行顺序是:

try/catch的return语句执行之后,但不直接返回,转而执行finally,然后才return结果。

finalize关键字

Java允许在类中定义一个名为finalize()的方法。它的工作原理是:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法。并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。

关于垃圾回收,有三点需要记住:

1、对象可能不被垃圾回收。这里有一个潜在的编程陷阱,因为有些程序员(特别是 C++程序员)刚开始可能会误把finalize( )当作C++中的“析构函数”(C++中销毁对象必须用到这个函数)。所以有必要明确区分一下:在 C++中,对象一定会被“销毁”(如果程序中没有错误的话);而 Java 里的对象却并非总是被“垃圾回收”的。只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。 

2、垃圾回收并不等于“析构”。

3、垃圾回收只与内存有关。使用垃圾回收的唯一原因是为了回收程序不再使用的内存。所以对于与垃圾回收有关的任何行为来说(尤其是finalize( )方法),它们也必须同内存及其回收有关。

类可以覆盖finalize()方法,在垃圾回收时定义一些本地的行为。

protected void finalize() throws Throwable {  
 /*close open files,release resources,etc*/
}  

overloading vs overriding

overloading

overloading是重载的意思。

overriding

overriding是覆盖的意思。

Collection Framework

ArrayList

Vector

LinkedList

ArrayList和LinkedList的大致区别: 1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 2.对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针。 3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。

LinkedList<String> myLinkList= new LinkedList<String>();
myLinkList.add("two");
myLinkList.addFirst("one");
myLinkList.addLast("three");//效果与add()一样
Iterator<String> iter =myLinkList.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}

HashMap

面试题

1. private类型的构造函数在继承里有什么作用?

将构造函数声明为private类型,将使得在类外不能直接实例化这个类。为这个类创建实例的唯一方法时提供一个public static方法,就像工厂函数模式的做法一样。进一步地,由于构造函数是私有的,因此该类不能被继承。

2. 如果我们在try/catch 块里插入return语句,finally块还会执行吗?

finally块当然会执行。即使我们打算在try里退出(return,continue,break,throw exception等),finally都会执行。

下面是finally块不会执行的情况:

  • 执行try/catch的进程被kill了。
  • 在执行try/catch的时候jvm退出了。

3. final ,finally,finalize的区别

虽然它们长得挺像,但各自的功能差别巨大。

  • final final primitive变量:你不能改变它的值 final 引用变量:该引用不能再只想堆上的其它对象 final的method:你不能覆盖该method final的类:你不能继承该类,也就是创建它的子类。
  • finally 在try/catch块后面可选的加上finally块,finally块总会执行,除非java虚拟机从try/catch块中退出了。finally块一般用来做一些代码清理工作。
  • finalize() 当对象没有引用存在时,垃圾回收器会调用finalize()函数。它的典型应用是清理资源,例如关闭文件。

4. c++ template和java 泛型的区别

很多程序员简单的认为c++ template和java 泛型是等价的,因为他们都允许你使用List< String>这样的语句,但是两个语言的实现方式和理由是完全不同的。

java泛型根植于“类型擦除”这个理念。当java源代码被转化成jvm二进制代码时,类型参数就被擦除了。 例如,下面的java代码:

Vector <String> vector=new Vector<String>();
vector.add(new String("hello"));
String str = vector.get(0);

在编译期间,代码将被转化成:

Vector vector = new Vector();
vector.add(new String("hello"));
String str = (String) vector.get(0);

java泛型有时候也被称为语法糖。

C++的泛型(模板)则很不相同,编译器将为每种类型创造一个template副本,举个例子,C++将为MyClass< Foo>和MyClass< Bar>创造两个模板副本,如果MyClass有static类型的变量的话,MyClass< Foo>和MyClass< Bar>将不会共享这个变量,然而在MyClass< Foo>的实例会共享MyClass< Foo>的static变量。

在java里,static类型的变量将被MyClass的所有实例所共享,不管MyClass的类型参数是什么。

由于体系结构上的差异,java 泛型和c++ 模板的区别还有很多区别:

  • c++ template使用主数据类型,例如 int,而java则要用Integer。
  • java ,所有MyClass的实例,都是相同的类型,不管类型参数是什么,类型参数在运行时被擦除了。而在c++里不同类型参数的类的实例属于不同的类。
  • java,你可以限制类型参数为特定的类型,例如
public <T extends Animal> void takeThing (ArrayList<T> list)
  • java 类型参数,如MyClass< Foo>中的Foo不能用于static类型的方法和变量中,因为static将被MyClass< Foo>和MyClass< Bar>所共享,而在c++里,MyClass< Foo>和MyClass< Bar>是不同的类,因此类型参数Foo可以用在static的方法和变量里。
  • 在java里,所有MyClass的实例都是同类的,与类型参数无关,类型参数在运行时被擦除了。而c++里,不同的类型参数导致了不同的类。

记住,尽管java泛型跟c++ template有很多相似的地方,但它们其实非常不同。

5. java的object reflection

object reflection是java的一个特征,它提供了一种动态获取某个类的类信息的方式。

一般有这几种用法:

1.在运行时获取类的某个方法或字段。你也许不知道它的用法,但知道名字。

2.创建一个新的类实例。

3.直接getting or setting对象的某个字段,而不管它的访问修饰符是什么。

  • java.lang.Class Class 类的实例表示正在运行的Java应用程序中的类和接口。这个类就是类对象,是具体某个类对象,通常我们所说的对象是,某个类的对象,而Class是类级别的对象,描述的类的信息。例如 String a=new String(); Class c = a.getClass(); 这个a指的是类String的对象,那么什么是类对象呢,看这个例子: Class c=String.class; 这个c就是String的类对象,描述的是String的信息。不是对象信息。

  • Class forName(String className): 返回与带有给定字符串名的类或接口相关联的 Class 对象。

例如,以下代码片段返回命名为 java.lang.Thread 的类的运行时 Class 描述符。

   Class t = Class.forName("java.lang.Thread")
   System.out.println(t);
   //class java.util.Thread

看下面一段例子程序,很好的演示了对象反射的应用:

Object [] doubleArgs = new Object [] {4.2,3.9};
Class rectangleDefinition = Class.forName("MyProj.Rectangle");
Class [] doubleArgsClass = new Class [] {double.class, double.class};
Constructor doubleArgsConstructor = rectangleDefinition.getConstructor(doubleArgsClass);
Rectangle rectangle = (Rectangle) doubleArgsConstructor.newIntance(doubleArgs);
Method m = rectangleDefinition.getDeclaredMethod("area");
Double area= (Double) m.invoke(rectangle);

上面这段代码的作用与下面等价:

Rectangle rectangle = new Rectangle(4.2,3.9);
Double area= rectangle.area();

也许上面这个例子并没有让你觉得 object reflection有多有用,但在某些特殊的场合,它的作用是明显的:

  • 帮助我们在运行时观察或操作程序的行为
  • 帮助我们调试或测试程序,因为我们可以直接访问方法,构造函数,以及字段。
  • 当我们事先不知道方法的细节时,我们可以通过方法的名字来调用函数。例如,我们可以向构造函数传入类名,构造参数,来创建一个新实例,也可以通过函数的名字来调用函数。如果没有object reflection,这样的事情可能会花费很多工作,甚至根本无法完成。

6. 实现一个CircularArray 类支持array-like的数据结构,并且可以高效的循环。这个类要能够支持泛型,并且支持迭代器语法 for(Obj o : circularArray)

这个问题可以分为两部分,第一部分是实现循环,第二部分实现迭代器。

public class CircularArray<T>{
private T [] items;
private int head=0;

public CircularArray(int size){
items=(T []) new Object [size];
}

private int convert(int index){
if(index<0){
index=index+items.length;}
return (head+index)%items.length;
}

public void rotate(int shiftRight){
head=convert(shiftRight);
}

public T get(int i ){
if (i<0 || i >=items.length){
throw new java.lang.IndexOutBoundsException();}
}
return items[convert(i)];

public void set (int i ,T item){
items[convert(i)]=item;
}

}

convert()函数很好的达到了函数重用的目的。注意head的作用。我们可以通过增加head来达到循环的目的。

接下来是实现Iterator,使得可以使用for循环。为此,我们将实现下面的功能:

  • implement Iterator类,因此需要实现iterator()方法。
  • 为了实现iterator()方法,通常需要实现一个私有类,包含hasNext(),next()。
public class CircularArray<T> implements Iterator<T>{
...
public Iterator<T> iterator(){
return new CircularArrayIterator<T>(this);
}

private calss CircularArrayIterator<TI> implements Iterator<TI>{

private int _current=-1;
private TI [] _items;

public CircularArrayIterator(CircularArray<TI> array){
_items = array.items;
}

public boolean hasNext(){
return _current<_items.length-1;
}

public TI next(){
_current++;
TI item=(TI) _items[convert(_current)];
return item;}

}
}

当使用

CircularArray<String> array = ...;
for (String s : array){...}

时,将调用hasNext(),next()

Comments !